import { Layout } from '@/layout';
import { MDX_DATA } from '@/mdx';

export default Layout(MDX_DATA.VanillaExtract);

# Vanilla extract integration

[Vanilla extract](https://vanilla-extract.style/) is a TypeScript CSS preprocessor that generates static CSS files at build time.
It is a great alternative to [CSS Modules](/styles/css-modules) if you prefer to write your styles in TypeScript.

## Vanilla extract vs CSS Modules

[Vanilla extract](https://vanilla-extract.style/) and [CSS Modules](/styles/css-modules) do the same thing,
but with different syntax. Common features of [Vanilla extract](https://vanilla-extract.style/) and [CSS Modules](/styles/css-modules):

- Styles are generated at build time – no runtime and performance overhead
- Classes names are scoped to the styles file

Differences between [Vanilla extract](https://vanilla-extract.style/) and [CSS Modules](/styles/css-modules):

- Vanilla extract styles are type safe
- You can use any JavaScript/TypeScript code in Vanilla extract styles, including [color functions](/styles/color-functions)
- With Vanilla extract you do not have access to [postcss-preset-mantine](/styles/postcss-preset) features like `light-dark` function and `hover` mixin.
  Because of this, you will not be able to copy-paste all demos from Mantine documentation and use them with Vanilla extract.
- Vanilla extract requires additional configuration and setup that may not be available for your build tool/framework.
  Most popular tools like [Next.js](https://nextjs.org/) and [Vite](https://vitejs.dev/) have plugins for Vanilla extract,
  but if you are using something more niche, you might need to configure it yourself.

Note that you can use both [Vanilla extract](https://vanilla-extract.style/) and [CSS Modules](/styles/css-modules) in the same project,
it will not cause any issues: performance will be the same and the bundle size will not be impacted.

## Installation

Follow the [installation instructions](https://vanilla-extract.style/documentation/getting-started) to install vanilla extract.
Then install `@mantine/vanilla-extract` package, it exports `themeToVars` function to convert Mantine theme to CSS variables:

<InstallScript packages="@mantine/vanilla-extract" />

## Templates

You can use one of the following templates to get started or a reference for your own setup.
Note that all templates include only minimal setup.

<TemplatesList
  name={[
    'vite-vanilla-extract-template',
    'next-vanilla-extract-template',
  ]}
/>

## Theming

Vanilla extract provides [createTheme](https://vanilla-extract.style/documentation/theming/)
function which converts given theme object into CSS variables and assigns them to `:root` or other selector.
You should not use Vanilla extract `createTheme` to generate Mantine theme tokens – all Mantine [theme](/theming/theme-object)
properties are already exposed as CSS variables. Instead, use `themeToVars` function from `@mantine/vanilla-extract` package
to create an object with CSS variables from Mantine theme:

```tsx
// theme.ts
import { createTheme } from '@mantine/core';

// Do not forget to pass theme to MantineProvider
export const theme = createTheme({
  fontFamily: 'serif',
  primaryColor: 'cyan',
});
```

```tsx
// theme.css.ts
import { theme } from './theme';
import { themeToVars } from '@mantine/vanilla-extract';

// CSS variables object, can be access in *.css.ts files
export const vars = themeToVars(theme);
```

## Styling

Import `vars` object in `*.css.ts` files to access Mantine [CSS variables](/styles/css-variables):

```tsx
// Demo.css.ts
import { style } from '@vanilla-extract/css';
import { vars } from './theme';

export const demo = style({
  fontSize: vars.fontSizes.xl,
  backgroundColor: vars.colors.red[5],
  color: vars.colors.white,
});
```

## rem and em

To convert px to [rem or em](/styles/rem) use `rem` and `em` functions from `@mantine/core` package:

```tsx
// Demo.css.ts
import { style } from '@vanilla-extract/css';
import { rem } from '@mantine/core';

export const demo = style({
  fontSize: rem(16),

  '@media': {
    [`(min-width: ${em(768)})`]: {
      fontSize: rem(18),
    },
  },
});
```

## light and dark selectors

`vars` object contains `lightSelector` and `darkSelector` properties which can be used to
apply styles only in light or dark color scheme:

```tsx
// Demo.css.ts
import { style } from '@vanilla-extract/css';
import { vars } from './theme';

export const demo = style({
  fontSize: vars.fontSizes.xl,

  selectors: {
    [vars.lightSelector]: {
      backgroundColor: vars.colors.red[5],
      color: vars.colors.white,
    },

    [vars.darkSelector]: {
      backgroundColor: vars.colors.blue[5],
      color: vars.colors.white,
    },
  },
});
```

Note that usually it is more convenient to use only one of them:
apply styles for light color scheme and then override them for dark color scheme
with `vars.darkSelector` (or vice versa):

```tsx
// Demo.css.ts
import { style } from '@vanilla-extract/css';
import { vars } from './theme';

export const demo = style({
  fontSize: vars.fontSizes.xl,
  backgroundColor: vars.colors.red[5],
  color: vars.colors.white,

  selectors: {
    [vars.darkSelector]: {
      backgroundColor: vars.colors.blue[5],
      color: vars.colors.white,
    },
  },
});
```

## largerThan and smallerThan

`vars` object contains `largerThan` and `smallerThan` properties which can be used in
`@media` as a shorthand for `min-width` and `max-width`:

```tsx
// Demo.css.ts
import { style } from '@vanilla-extract/css';
import { vars } from './theme';

export const demo = style({
  fontSize: vars.fontSizes.sm,

  '@media': {
    // equivalent to `(min-width: 640px)` converted to em
    // -> `(min-width: 40em)`
    [vars.largerThan(640)]: {
      fontSize: vars.fontSizes.md,
    },

    // equivalent to `(max-width: 640px)` converted to em
    // -> `(max-width: 40em)`
    [vars.smallerThan(640)]: {
      fontSize: vars.fontSizes.xs,
    },

    // You can reference `theme.breakpoints` values
    [vars.largerThan('sm')]: {
      fontSize: vars.fontSizes.md,
    },
  },
});
```

## rtl selector

Use `vars.rtlSelector` to apply styles only in rtl direction:

```tsx
// Demo.css.ts
import { style } from '@vanilla-extract/css';
import { vars } from './theme';

export const demo = style({
  paddingRight: vars.spacing.md,

  selectors: {
    [vars.rtlSelector]: {
      paddingLeft: vars.spacing.md,
      paddingRight: 0,
    },
  },
});
```
